#ifndef MMU_H
#define MMU_H

#include "PageTable.h"
#include "TLB.cpp"
#include "MM.h"

using namespace std;

const int PG_LRUPURE           = 0;
const int PG_LRUAPPROX         = 1;
const int PG_FIFO              = 2;
const int PG_WORKINGSET        = 3;

// gap (in ticks) for lruApprox before entries applicable for removal (unless nothing is below, then lowest chosen)
const int gapThreshold         = 10;

const int WS_TAU               = 4; // Time delta in Working Set

const int N = 6;
//const int F = framesPerProc - 3;
const int G = 3;

// Lookup return struct
struct LookupReturn {
  int ret;
  int frame;
};


// Lookup return values. Read as NEED (ONLY) INSERT or NEED REPLACE
const int LOOKUP_IN_MM         = 0; // Do nothing
const int LOOKUP_INSERT        = 1; // Add frame to MM, update PageTable
const int LOOKUP_REPLACE       = 2; // Replace frame from MM, update PageTable

struct PraReturn {
  int ret; // PRA_CLEAN or PRA_DIRTY, depending on dirty bit of removed frame
  int frame;
};

// PRA return values
const int PRA_CLEAN = 1;
const int PRA_DIRTY = 4;

class MMU {
 private:
   int replacementPolicy;
   PageTable* pt;
   TLB* tlbCopy;
   MM* mm;
   void removeFromTlb(long page);
   LookupReturn PTlookupLruPure(long page, bool read, int tick);
   LookupReturn PTlookupLruApprox(long page);
   LookupReturn PTlookupWorkingSet(long page, bool read, int tick);      
   LookupReturn PTlookupFIFO(long page, bool read, int tick);   

   int FindOpeningsLruApprox(int ibase, int ibound);
   
   int insertNewPageLruPure(long page,int frame, int tick, bool read);
   int insertNewPageLruApprox(long page, int frame, int tick, bool read);
   int insertNewPageWorkingSet(long page,int frame, int tick, bool read);
   int insertNewPageFIFO(long page,int frame, int tick, bool read);
   
   PraReturn LruPure();
   PraReturn LruApprox(int itick);
   PraReturn Fifo();
   PraReturn WorkingSet(int itick);
   int framesPerProcess; //  number of MM frames allowed for each process (MM Size / # processes)
   int TlbHitCount;
   int TlbMissCount;
   int PageInCount;
   int PageOutCount;

 public:
  MMU(PageTable* ptable, TLB* t, MM* mm);
  void daemon(int ptick, int tick);
  void setPolicy(int policy)
  {
    cout << "Setting policy: " << policy << endl;
    replacementPolicy = policy;
  }
  void setTlb(TLB* t);
  void setFramesPerProcess(int iframes){ framesPerProcess = iframes;};
  const int getPolicy(){ return replacementPolicy;}  
  int findAddress(bool read, int virtualAddress, int ibase, int ibound, int itick);
  int getFramesPerProcess(){ return framesPerProcess;}
  int returnTlbHitCount() { return TlbHitCount; }
  int returnTlbMissCount() { return TlbMissCount; }
  int returnPageInCount() { return PageInCount; }
  int returnPageOutCount() { return PageOutCount; }
};


#endif
